#include <stdio.h>

#define LGOGL_CORE

#include "lgogl_texture.h"

static GLint
switchFormat(int format) {
    switch (format) {
    case LGTEX_RGBA:
        return GL_RGBA;
    case LGTEX_RGB:
        return GL_RGB;
    default:
        return GL_RGBA;
    }
}

static GLint
switchWrap(int wrap) {
    switch (wrap) {
    case LGTEX_WRAP_BORDER:
        return GL_CLAMP_TO_BORDER;
    case LGTEX_WRAP_EDGE:
        return GL_CLAMP_TO_EDGE;
    case LGTEX_WRAP_REPEAT:
        return GL_REPEAT;
    default:
        return GL_REPEAT;
    }
}

static size_t
sizeofTexture() {
    return sizeof(LGOGLlTexture);
}

static int
newTexture(LGTexture* tex) {
    LGOGLlTexture* oglTex = (LGOGLlTexture*)tex;
    glGenTextures(1, &oglTex->glId);
    glBindTexture(GL_TEXTURE_2D, oglTex->glId);
    GLint minFilter = (tex->minFilter == LGTEX_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST;
    GLint maxFilter = (tex->maxFilter == LGTEX_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter);
    GLfloat borderColor[4] = { 1.f, 1.f, 1.f, 1.f };
    glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, switchWrap(tex->sWrap));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, switchWrap(tex->tWrap));
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
        tex->width, tex->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    GLint err = glGetError();
    if (err) {
        printf("newTexture err:%x\n", err);
    }
    return 0;
}


static int
uploadPixel(LGTexture* tex,
    int format, unsigned rect[4], const void* data) {
    LGOGLlTexture* oglTex = (LGOGLlTexture*)tex;
    glBindTexture(GL_TEXTURE_2D, oglTex->glId);
    glTexSubImage2D(GL_TEXTURE_2D, 0,
        rect[0], rect[1], rect[2], rect[3],
        switchFormat(format), GL_UNSIGNED_BYTE, data);
    GLint err = glGetError();
    if (err) {
        GL_INVALID_ENUM;
        printf("uploadPixel err:%x\n", err);
    }
    return 0;
}

static void
setFilter(LGTexture* tex) {
    LGOGLlTexture* oglTex = (LGOGLlTexture*)tex;
    glBindTexture(GL_TEXTURE_2D, oglTex->glId);
    GLint minFilter = (tex->minFilter == LGTEX_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST;
    GLint maxFilter = (tex->maxFilter == LGTEX_FILTER_LINEAR) ? GL_LINEAR : GL_NEAREST;
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, minFilter);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, maxFilter);
}

static void
setWrap(LGTexture* tex) {
    LGOGLlTexture* oglTex = (LGOGLlTexture*)tex;
    glBindTexture(GL_TEXTURE_2D, oglTex->glId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, switchWrap(tex->sWrap));
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, switchWrap(tex->tWrap));
}

static int
freeTexture(LGTexture* tex) {
    LGOGLlTexture* oglTex = (LGOGLlTexture*)tex;
    glDeleteTextures(1, &oglTex->glId);
    return 0;
}


LGTextureBackend backend = {
    sizeofTexture,
    newTexture,
    freeTexture,
    uploadPixel,
    setFilter,
    setWrap,
};

LGOGL_API LGTextureBackend*
lgoglTextureBackend(){
    return &backend;
}

LGOGL_API GLuint
lgoglGetTextureId(LGTexture* tex) {
    return ((LGOGLlTexture*)tex)->glId;
}
